Release 10.1A: OpenEdge Getting Started:
Object-oriented Programming


Errors within a constructor

There are two types of errors that can occur during the creation of an object:

Handling instantiation errors

If an error causes the Progress session not to instantiate the object, the error is automatically propagated back to the NEW statement. There are several reasons why the session might not be able to instantiate an object. One of the constructors in the class hierarchy might not have the proper signature (mismatched number of parameters, or mismatched data type of a parameter), for example. In this case, when the Progress session determines that it cannot invoke a constructor because of one of these errors, the error cannot be handled on the SUPER statement that invoked the constructor, but instead can only be handled by the NEW statement that instantiated the object. For this reason, it is illegal to put NO-ERROR on the SUPER statement.

Note: An improper signature can appear in a previously compiled class hierarchy if out-dated r-code for one of the classes is found and executed on the PROPATH. Thus, you must ensure that all classes in a class hierarchy are properly compiled. For more information, see the "Compiling class files" section.

When the Progress session encounters an instantiation error, it raises ERROR on the NEW statement and sets the object reference to the Unknown value (?). How an application handles this error is thus controlled by the NEW statement.

The following example (instantiater1.p) assumes that the class Derived cannot be instantiated because the signature of one of its constructors is invalid. In this case, Progress sets rDerived to the Unknown value (?) and sets the ERROR STATUS system handle with the corresponding error information, as shown:

instantiater1.p
DEFINE VARIABLE rDerived AS CLASS Derived NO-UNDO. 
DEFINE VARIABLE bSuccess AS LOGICAL NO-UNDO. 
rDerived = NEW Derived (INPUT "fred", OUTPUT bSuccess) NO-ERROR. 
IF ERROR-STATUS:ERROR THEN /* Was the class instantiated successfully? */ 
    MESSAGE ERROR-STATUS:GET-MESSAGE(1). 

In instantiater1.p, Progress knows that the object has not been instantiated at all. Progress sets the object reference (rDerived) to the Unknown value (?) and does not invoke any of the class destructors. This is because Progress also knows that none of the corresponding constructors in the class hierarchy had an opportunity to run successfully. The procedure then checks the ERROR-STATUS system handle to verify the success of the NEW statement, and proceed accordingly.

Handling application errors

When an application determines that the instantiation process is not proceeding properly because of an application error, it is obligated to inform the caller of this condition from the constructor. The same restrictions on returning an error from a method also apply to constructors. This means that during the object instantiation process, if the constructor is unable to perform its function to either populate the object's data members or perform any other object initialization task, it must return status information to the caller using an output parameter, setting a public data member, or using some other technique, in order to convey this error state.

The previous section shows examples of setting an output parameter to handle errors in methods (see the "Errors within a method" section). You can use a similar mechanism to indicate an error in a constructor.

In the following examples (Base.cls and Derived.cls), one object (Derived) inherits behavior from another object (Base). In this instance, the Base constructor fails to initialize properly because the FIND statement fails to return a record:

Base.cls
CLASS Base: 
    CONSTRUCTOR PUBLIC Base (INPUT iName AS CHARACTER, 
                             OUTPUT bSuccess AS LOGICAL, 
                             OUTPUT id AS INTEGER): 
        id = ?. 
        bSuccess = FALSE. 
        FIND Customer WHERE Customer.NAME BEGINS iName NO-ERROR. 
        IF AVAILABLE(Customer) THEN DO: 
            id = customer.cust-num. 
            bSuccess = TRUE. 
        END. 
    END CONSTRUCTOR. 
END CLASS. 

Derived.cls
CLASS Derived INHERITS Base: 
    CONSTRUCTOR PUBLIC Derived(INPUT iName AS CHARACTER,  
                               OUTPUT bSuccess AS LOGICAL): 
        DEFINE VARIABLE id AS INTEGER NO-UNDO. 
        SUPER(INPUT iName, OUTPUT bSuccess, OUTPUT id). 
        IF (bSuccess) THEN DO: 
            /* Perform this class's initialization. 
               If this class's initialization fails, 
               this initialization might set bSuccess to FALSE. 
            */ 
        END. 
    END CONSTRUCTOR. 
END CLASS. 

instantiater2.p
DEFINE VARIABLE rDerived AS CLASS Derived NO-UNDO. 
DEFINE VARIABLE bSuccess AS LOGICAL NO-UNDO. 
rDerived = NEW Derived (INPUT "fred", OUTPUT bSuccess) NO-ERROR. 
IF VALID-OBJECT (rDerived) THEN /* Was the class instantiated successfully? */ 
    IF NOT bSuccess THEN DO: /* Did initialization complete successfully? */ 
        DELETE OBJECT rDerived. 
        rDerived = ?. 
    END. 

In instantiater2.p, the Progress session does not know that the object failed to instantiate properly. Therefore, Progress provides rDerived with a valid object reference. The only reason that the caller knows that the instantiation process failed, is that each class in the hierarchy propagates the bSuccess output parameter back to the NEW statement.

Using this model, every level of the class hierarchy must define a constructor output parameter and pass up the status value all the way to the NEW statement. This can be cumbersome in a class hierarchy with many levels, where you might want to use an alternative.

As one alternative, you can terminate the instantiation process by deleting the object being instantiated from within a constructor during the instantiation process. If a constructor invokes the DELETE OBJECT THIS-OBJECT statement, the Progress session interprets this statement as an application request to terminate the instantiation process. When the constructor that invoked DELETE OBJECT completes, the Progress session does not continue to execute the calling constructor (the constructor whose SUPER statement invoked the super class constructor that failed). Instead, Progress terminates the remaining constructors on the call stack and continues from the NEW statement that invoked them.

In this situation, the Progress session sets the object reference assigned by the NEW statement to the Unknown value (?), but does not raise ERROR. In addition, the Progress session executes all of the destructors for all of the classes in the hierarchy that executed prior to the constructor that invoked the DELETE OBJECT statement. This gives each initialized class in the hierarchy an opportunity to free any resources that its corresponding constructor might have allocated.

In the following example, instantiater3.p, assume that the class Derived cannot be instantiated because one of its constructors called DELETE OBJECT THIS-OBJECT. Progress sets rDerived to the Unknown value (?), but does not set ERROR-STATUS system handle, as shown:

instantiater3.p
DEFINE VARIABLE rDerived AS CLASS Derived NO-UNDO. 
DEFINE VARIABLE bSuccess AS LOGICAL NO-UNDO. 
rDerived = NEW Derived (INPUT "fred", OUTPUT bSuccess) NO-ERROR. 
IF rDerived EQ ? /* Was the class instantiated successfully? */ 
    THEN ... /* Object instantiation failed */  

In instantiater3.p, Progress knows that the object has not been instantiated. Therefore, it sets the object reference (rDerived) to the Unknown value (?) and invokes the destructor for any class in the hierarchy whose constructor executed completely.


Copyright © 2005 Progress Software Corporation
www.progress.com
Voice: (781) 280-4000
Fax: (781) 280-4095